package io.gitee.kotle.player.download.impl.exoplayer

import android.annotation.SuppressLint
import android.content.Context
import android.os.Environment
import androidx.media3.common.MediaItem
import androidx.media3.database.DatabaseProvider
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.datasource.cache.Cache
import androidx.media3.datasource.cache.CacheEvictor
import androidx.media3.datasource.cache.NoOpCacheEvictor
import androidx.media3.datasource.cache.SimpleCache
import androidx.media3.exoplayer.offline.DownloadManager
import androidx.media3.exoplayer.offline.Download
import androidx.media3.exoplayer.scheduler.Requirements
import io.gitee.kotle.player.download.IPlayerDownload
import io.gitee.kotle.player.download.impl.BasePlayerDownload
import io.gitee.kotle.player.download.toDownloadRequest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.*
import java.lang.Exception
import java.util.concurrent.Executor
import java.util.concurrent.Executors

/**
 *
 * 描述：下载支持Exoplayer的资源管理器
 *
 * @author Jiale.Wei
 *
 * 创建时间：2023/4/27 17:27
 *
 */
open class ExoPlayerDownloadImpl(val context: Context) : BasePlayerDownload<MediaItem>(),
    DownloadManager.Listener {
    private companion object {
        private val cacheMap = mutableMapOf<String, Cache>()
    }

    /**
     * 下载管理器
     */
    val downloadManager by lazy {
        onCreateDownloadManager().also {
            it.addListener(this)
            //  it.resumeDownloads()
        }
    }

    override var maxParallelDownloads: Int
        get() = downloadManager.maxParallelDownloads
        set(value) {
            downloadManager.maxParallelDownloads = value
        }

    //----------------------------------监听器回调 start--------------------------
    override fun onInitialized(downloadManager: DownloadManager) {
        super.onInitialized(downloadManager)
    }

    override fun onDownloadsPausedChanged(
        downloadManager: DownloadManager,
        downloadsPaused: Boolean
    ) {
        super.onDownloadsPausedChanged(downloadManager, downloadsPaused)
        if (downloadsPaused) {
            cancelLoop()
            //暂停后，job取消 做最后一次更新
            scope.launch { onLoopRun(this) }
        } else {
            startLoop()
        }
    }

    override fun onDownloadChanged(
        downloadManager: DownloadManager,
        download: Download,
        finalException: Exception?
    ) {
        super.onDownloadChanged(downloadManager, download, finalException)
        updateSingleDownloadChange(download)
    }

    override fun onDownloadRemoved(downloadManager: DownloadManager, download: Download) {
        super.onDownloadRemoved(downloadManager, download)
        updateSingleDownloadChange(download)
    }

    override fun onIdle(downloadManager: DownloadManager) {
        super.onIdle(downloadManager)
    }

    override fun onRequirementsStateChanged(
        downloadManager: DownloadManager,
        requirements: Requirements,
        notMetRequirements: Int
    ) {
        super.onRequirementsStateChanged(downloadManager, requirements, notMetRequirements)
    }

    override fun onWaitingForRequirementsChanged(
        downloadManager: DownloadManager,
        waitingForRequirements: Boolean
    ) {
        super.onWaitingForRequirementsChanged(downloadManager, waitingForRequirements)
    }

    override suspend fun onLoopRun(scope: CoroutineScope) {
        super.onLoopRun(scope)
        //处于空闲状态或者暂停状态，不做回调
        if (downloadManager.downloadsPaused || downloadManager.isIdle) {
            return
        }
        listeners.toList().forEach {
            withContext(Dispatchers.IO) {
                val listener = it.second
                val allDownload = getAllDownloads()
                val currentDownload = downloadManager.currentDownloads
                withContext(Dispatchers.Main) { listener.onDownloadList(allDownload) }
                val mediaId = it.first
                currentDownload.forEach {
                    if (mediaId == null || mediaId == it.request.id) {
                        val downloadInfo = toDownloadInfo(it)
                        withContext(Dispatchers.Main) { listener.onDownload(downloadInfo) }
                    }
                }
            }
        }
    }

    /**
     * 单个更新
     * 主要处理下载移除，下载完成等情况（这种情况下载，就不在[downloadManager.currentDownloads]）
     */
    private fun updateSingleDownloadChange(download: Download) {
        scope.launch {
            listeners.toList().forEach {
                withContext(Dispatchers.IO) {
                    val listener = it.second
                    val allDownload = getAllDownloads()
                    withContext(Dispatchers.Main) { listener.onDownloadList(allDownload) }
                    val mediaId = it.first
                    if (mediaId == null || mediaId == download.request.id) {
                        val downloadInfo = toDownloadInfo(download)
                        withContext(Dispatchers.Main) { listener.onDownload(downloadInfo) }
                    }
                }
            }
        }
    }

    //----------------------------------监听器回调 end--------------------------
    /**
     * 下载索引
     */
    val downloadIndex
        get() = downloadManager.downloadIndex

    /**
     * 创建下载管理器
     */
    open fun onCreateDownloadManager() = DownloadManager(
        context,
        onCreateDatabaseProvider(),
        onCreateCache(),
        onCreateFactory(),
        onCreateExecutor()
    )

    /**
     * 创建网络工厂
     */
    open fun onCreateFactory(): DataSource.Factory {
        return DefaultHttpDataSource.Factory()
    }

    /**
     * 创建下载执行器
     */
    open fun onCreateExecutor(): Executor {
        return Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
    }

    /**
     * 创建缓存
     */
    open fun onCreateCache(): Cache {
        val dir = onCreateDownloadDir()
        val dirPath = dir.absolutePath
        val cache = cacheMap[dirPath]
        return cache ?: SimpleCache(
            onCreateDownloadDir(),
            onCreateCacheEvictor(),
            onCreateDatabaseProvider()
        ).also {
            cacheMap[dirPath] = it
        }
    }

    /**
     * 创建缓存文件夹
     */
    open fun onCreateDownloadDir(): File {
        val dir =
            context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!.absolutePath.trimEnd('/')
        return File("${dir}/ExoPlayerDownload/")
    }

    /**
     * 创建缓存清除器
     */
    open fun onCreateCacheEvictor(): CacheEvictor {
        return NoOpCacheEvictor()
    }

    /**
     * 创建数据库供应商
     */
    open fun onCreateDatabaseProvider(): DatabaseProvider {
        return StandaloneDatabaseProvider(context)
    }

    /**
     * 添加下载
     */
    override fun addDownload(mediaId: String, media: MediaItem, extra: ByteArray?, reason: Int?) {
        val stopReason = reason ?: Download.STOP_REASON_NONE
        downloadManager.addDownload(media.toDownloadRequest(mediaId, extra), stopReason)
        if (stopReason == Download.STOP_REASON_NONE) {
            resumeDownloads()
        }
    }

    /**
     * 获取当前下载
     */
    override fun getDownload(mediaId: String): IPlayerDownload.DownloadInfo<MediaItem>? {
        val download = downloadIndex
            .getDownload(mediaId)
        return if (download != null) {
            toDownloadInfo(download)
        } else {
            null
        }
    }

    /**
     * 获取当前下载
     */
    override fun getDownload(
        mediaId: String,
        block: (IPlayerDownload.DownloadInfo<MediaItem>?) -> Unit
    ) {
        scope.launch {
            val result = withContext(Dispatchers.IO) {
                getDownload(mediaId)
            }
            block.invoke(result)
        }
    }

    /**
     * 根据状态查询下载
     */
    @SuppressLint("WrongConstant")
    override fun getDownloadsByStates(states: List<IPlayerDownload.State>): MutableList<IPlayerDownload.DownloadInfo<MediaItem>> {
        val tempDownloads = mutableListOf<IPlayerDownload.DownloadInfo<MediaItem>>()
        val queryState = states.map {
            when (it) {
                IPlayerDownload.State.Completed -> Download.STATE_COMPLETED
                IPlayerDownload.State.Downloading -> Download.STATE_DOWNLOADING
                IPlayerDownload.State.Failed -> Download.STATE_FAILED
                IPlayerDownload.State.Queued -> Download.STATE_QUEUED
                IPlayerDownload.State.Removing -> Download.STATE_REMOVING
                IPlayerDownload.State.Restarting -> Download.STATE_RESTARTING
                IPlayerDownload.State.Stopped -> Download.STATE_STOPPED
                IPlayerDownload.State.Removed -> Download.STATE_REMOVING
            }
        }.toIntArray()
        downloadManager.downloadIndex.getDownloads(*queryState).use { cursor ->
            while (cursor.moveToNext()) {
                tempDownloads.add(toDownloadInfo(cursor.download))
            }
        }
        return tempDownloads
    }

    /**
     * 根据状态查询下载
     */
    @SuppressLint("WrongConstant")
    override fun getDownloadsByStates(
        states: List<IPlayerDownload.State>,
        block: (MutableList<IPlayerDownload.DownloadInfo<MediaItem>>) -> Unit
    ) {
        scope.launch(Dispatchers.IO) {
            val tempDownloads = getDownloadsByStates(states)
            withContext(Dispatchers.Main) {
                block.invoke(tempDownloads)
            }
        }
    }

    override fun getAllDownloads(): MutableList<IPlayerDownload.DownloadInfo<MediaItem>> {
        return getDownloadsByStates(
            listOf(
                IPlayerDownload.State.Completed,
                IPlayerDownload.State.Downloading,
                IPlayerDownload.State.Failed,
                IPlayerDownload.State.Queued,
                IPlayerDownload.State.Removing,
                IPlayerDownload.State.Restarting,
                IPlayerDownload.State.Stopped,
            )
        )
    }

    /**
     * 查询所有下载
     */
    override fun getAllDownloads(block: (MutableList<IPlayerDownload.DownloadInfo<MediaItem>>) -> Unit) {
        getDownloadsByStates(
            listOf(
                IPlayerDownload.State.Completed,
                IPlayerDownload.State.Downloading,
                IPlayerDownload.State.Failed,
                IPlayerDownload.State.Queued,
                IPlayerDownload.State.Removing,
                IPlayerDownload.State.Restarting,
                IPlayerDownload.State.Stopped,
            ), block
        )
    }

    /**
     * 获取当前下载
     */
    override fun getCurrentDownloads(): MutableList<IPlayerDownload.DownloadInfo<MediaItem>> {
        return downloadManager.currentDownloads.map { toDownloadInfo(it) }.toMutableList()
    }

    /**
     * 获取当前下载
     */
    override fun getCurrentDownloads(block: (MutableList<IPlayerDownload.DownloadInfo<MediaItem>>) -> Unit) {
        scope.launch(Dispatchers.IO) {
            val result = getCurrentDownloads()
            withContext(Dispatchers.Main) {
                block.invoke(result)
            }
        }
    }

    /**
     * download转DownloadInfo
     */
    private fun toDownloadInfo(
        download: Download,
        removed: Boolean = false
    ): IPlayerDownload.DownloadInfo<MediaItem> {
        val state = if (removed) {
            IPlayerDownload.State.Removed
        } else {
            when (download.state) {
                Download.STATE_COMPLETED -> IPlayerDownload.State.Completed
                Download.STATE_DOWNLOADING -> IPlayerDownload.State.Downloading
                Download.STATE_FAILED -> IPlayerDownload.State.Failed
                Download.STATE_QUEUED -> IPlayerDownload.State.Queued
                Download.STATE_REMOVING -> IPlayerDownload.State.Removing
                Download.STATE_RESTARTING -> IPlayerDownload.State.Restarting
                Download.STATE_STOPPED -> IPlayerDownload.State.Stopped
                else -> IPlayerDownload.State.Removed
            }
        }
        return IPlayerDownload.DownloadInfo(
            download.request.id,
            download.request.toMediaItem(),
            state,
            download.request.data,
            download.bytesDownloaded,
            download.contentLength,
            download.startTimeMs,
            download.updateTimeMs,
        )
    }

    /**
     * 移除下载
     */
    override fun removeDownload(mediaId: String) {
        downloadManager.removeDownload(mediaId)
    }

    /**
     * 恢复下载
     */
    override fun resumeDownload(mediaId: String) {
        downloadManager.setStopReason(mediaId, Download.STOP_REASON_NONE)
        resumeDownloads()
    }

    /**
     * 恢复所有下载
     */
    override fun resumeDownloads() {
        if (downloadManager.downloadsPaused) {
            downloadManager.resumeDownloads()
        }
    }

    /**
     * 暂停所有下载
     */
    override fun pauseDownloads() {
        if (!downloadManager.downloadsPaused) {
            downloadManager.pauseDownloads()
        }
    }

    /**
     * 停止下载
     */
    override fun pauseDownload(mediaId: String, reason: Int) {
        downloadManager.setStopReason(mediaId, reason)
    }

    override fun removeAllDownloads() {
        downloadManager.removeAllDownloads()
    }

    /**
     * 释放资源
     */
    override fun release() {
        super.release()
        downloadManager.release()
    }
}